#include "Gracz.h"
#include "Plansza.h"
#include <algorithm>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>
#include <ctime>

Gracz::Gracz(Plansza *plansza, TURA id)
: plansza(plansza),identyfikator(id), turaZakonczona(true)
{
	;
}


void Czlowiek::ObslugaMyszy(WSPOLRZEDNE_EKRANOWE wspolrzedne)
{
	if(plansza->Pole(plansza->WspolrzednePlanszy(wspolrzedne))==identyfikator) //wskazano i kliknieto na pole aktualnego gracza
	{
		plansza->UstawZaznaczenie(wspolrzedne);
		plansza->Zaznaczaj(true); //zaznacz to pole
		plansza->GenerujPodpowiedzi(identyfikator);
	}
	else if(plansza->Pole(plansza->WspolrzednePlanszy(wspolrzedne))==PUSTE&&plansza->StanZaznaczenia()) //Jezeli kliknieto na puste pole i ma sie zaznaczone pionka
	{
		if(plansza->WykonajRuch(plansza->PobierzZaznaczenie(),plansza->WspolrzednePlanszy(wspolrzedne),identyfikator)==true)
		{
			turaZakonczona=true;
			plansza->Zaznaczaj(false);
			plansza->GenerujPodpowiedzi(identyfikator);
		
		}
	}


}	
std::pair<int,RUCH> Komputer::NegMax(PlanszaWirtualna model, int glebokosc, TURA gracz)
{
	RUCH najlepszyRuch;
	liczbaOdwiedzin++;
	if (model.IloscDostepnychRuchow(identyfikator)==0||model.IloscDostepnychRuchow((TURA)-identyfikator)==0||glebokosc==0||(ograniczCzas&&(time(0)-czasMaks>10)))
	{
		//	std::cout<<"OCENA ("<<glebokosc<<"): "<<Ocena(model,identyfikator,glebokosc)<<std::endl;
		return std::make_pair((int)gracz* Ocena(model,glebokosc),najlepszyRuch);
	}
	int najlepszy=-10000;

	std::vector<RUCH> ruchy=model.GenerujRuchy(gracz);
	//najlepszyRuch= ruchy.begin();
	for(std::vector<RUCH>::iterator it=ruchy.begin();it!=ruchy.end();it++)
	{
		PlanszaWirtualna modelZmodyfikowany(model);
		modelZmodyfikowany.WykonajRuch(it->zrodlo,it->cel,gracz);
		int wartosc = - NegMax(modelZmodyfikowany,glebokosc-1,(TURA)-gracz).first;
		if(wartosc>najlepszy) {
			najlepszy=wartosc;
			najlepszyRuch=(*it);
		}
	}
	return std::make_pair(najlepszy,najlepszyRuch);
}


int Komputer::Ocena(PlanszaWirtualna model, int glebokosc)
{
	int wartoscGleb=0;
	if (glebokosc)
	{
		if(model.IloscDostepnychRuchow(GRACZ_1)==0)
			wartoscGleb+=100;
		else
			wartoscGleb-=100;
	}
	return  wartoscGleb+model.IloscDostepnychPionow(GRACZ_2);//model.IloscDostepnychRuchow(gracz);//+5*model.IloscDostepnychPionow(gracz)+(glebokoscMax+(int)identyfikator-glebokosc);
}


std::pair<int,RUCH> Komputer::AlphaBeta(PlanszaWirtualna model,int glebokosc, TURA gracz, std::pair<int,RUCH> alpha,  std::pair<int,RUCH> beta)
{
	RUCH najlepszyRuch;
	liczbaOdwiedzin++;
	if (model.IloscDostepnychRuchow(identyfikator)==0||model.IloscDostepnychRuchow((TURA)-identyfikator)==0||glebokosc==0||(ograniczCzas&&(time(0)-czasMaks>10)))
	{
		return std::make_pair((int)gracz* Ocena(model,glebokosc),najlepszyRuch);
	}


	std::vector<RUCH> ruchy=model.GenerujRuchy(gracz);
	for(std::vector<RUCH>::iterator it=ruchy.begin();it!=ruchy.end();it++)
	{
		PlanszaWirtualna modelZmodyfikowany(model);
		modelZmodyfikowany.WykonajRuch(it->zrodlo,it->cel,gracz);
		int wartosc = - AlphaBeta(modelZmodyfikowany,glebokosc-1,(TURA)-gracz,std::make_pair(-beta.first,beta.second),std::make_pair(-alpha.first,alpha.second)).first;
		if(wartosc>alpha.first)
		{
			alpha.first=wartosc;
			alpha.second=*it;
		}
		if(alpha.first>=beta.first)
		{
			return beta;
		}
	}
	return alpha;
}

bool comp(std::pair<int,RUCH>  x,std::pair<int,RUCH>  y) { return x.first>y.first;};

void Mieszaj(std::vector<std::pair<int, RUCH> > * tablica)
{
	std::pair<int, RUCH> temp;
	if(tablica->size()>3){

		int wylosowana=rand()%2;
		int wylosowana2=rand()%2;


		temp=tablica->at(wylosowana);
		tablica->at(wylosowana)=tablica->at(wylosowana2);
		tablica->at(wylosowana2)=temp;
	}
}

std::pair<int,RUCH> Komputer::AlphaBetaHT(PlanszaWirtualna model,int glebokosc, TURA gracz, std::pair<int,RUCH> alpha,  std::pair<int,RUCH> beta)
{
	RUCH najlepszyRuch;
	liczbaOdwiedzin++;
	
	if (model.IloscDostepnychRuchow(identyfikator)==0||model.IloscDostepnychRuchow((TURA)-identyfikator)==0||glebokosc==0||(ograniczCzas&&(time(0)-czasMaks>10)))
	{
		return std::make_pair((int)gracz* Ocena(model,glebokosc),najlepszyRuch);
	}


	std::vector<RUCH> ruchy=model.GenerujRuchy(gracz);

	std::vector<std::pair<int,RUCH> > ruchyZOcena;

	for(std::vector<RUCH>::iterator it=ruchy.begin();it!=ruchy.end();it++)
	{
		bool dodano=false;
		for(std::vector<std::pair<int,RUCH>>::iterator itht=tablicaHistorii.begin();itht!=tablicaHistorii.end();itht++)
			if(itht->second==*it)
			{
				dodano=true;
				bool nieCykl=true;
				for(std::list<RUCH>::iterator lit=porzednieRuchy.begin();lit!=porzednieRuchy.end();lit++)
					if(*lit==*it)
						nieCykl = false;
				
				ruchyZOcena.push_back(std::make_pair(itht->first*(nieCykl),*it));
				break;
			}	
		
		if(!dodano)
		{
			//tablicaHistorii.push_back(std::make_pair(0,*it));
			ruchyZOcena.push_back(std::make_pair(0,*it));
		}

				
	}
	std::sort(ruchyZOcena.begin(),ruchyZOcena.end(),comp);
	Mieszaj(&ruchyZOcena);
	

	for(std::vector<std::pair<int,RUCH> > ::iterator it=ruchyZOcena.begin();it!=ruchyZOcena.end();it++)
	{
		PlanszaWirtualna modelZmodyfikowany(model);
		modelZmodyfikowany.WykonajRuch(it->second.zrodlo,it->second.cel,gracz);
		int wartosc = - AlphaBetaHT(modelZmodyfikowany,glebokosc-1,(TURA)-gracz,std::make_pair(-beta.first,beta.second),std::make_pair(-alpha.first,alpha.second)).first;
		if(wartosc>alpha.first)
		{
			alpha.first=wartosc;
			alpha.second=it->second;
		}
		if(alpha.first>=beta.first)
		{
			
			return beta;
		}
	}
	bool dodano=false;
	for(std::vector<std::pair<int,RUCH>>::iterator itht=tablicaHistorii.begin();itht!=tablicaHistorii.end();itht++)
	{
		if( alpha.second==itht->second)
		{
			itht->first+= 2<<((glebokoscMax-glebokosc)/2);
			dodano=true;
		}
		
	}
	if(!dodano)
		tablicaHistorii.push_back(std::make_pair(2<<((glebokoscMax-glebokosc)/2),alpha.second));

	return alpha;
}


void Komputer::RozpocznijTure()
{
	Gracz::RozpocznijTure();
	liczbaOdwiedzin=0; 

	czasMaks=time(0);

			
	RUCH dowolnyRuch;
	std::pair<int,RUCH> rz;
	switch(algorytm)
	{
	case 0: rz=NegMax((PlanszaWirtualna) *plansza,glebokoscMax,identyfikator); break;
	case 1: rz=AlphaBeta((PlanszaWirtualna) *plansza, glebokoscMax, identyfikator,std::make_pair(-1000,dowolnyRuch),std::make_pair(1000,dowolnyRuch)); break;
	case 2: rz=AlphaBetaHT((PlanszaWirtualna) *plansza, glebokoscMax, identyfikator,std::make_pair(-1000,dowolnyRuch),std::make_pair(1000,dowolnyRuch)); break;
	}
	RUCH znaleziony=rz.second;
	
	if(!plansza->WykonajRuch(znaleziony.zrodlo,znaleziony.cel,identyfikator))
	{std::cout<<znaleziony.zrodlo.ToString()<<" : "<<znaleziony.cel.ToString()<<std::endl;
		std::cout<<"BLEDNY RUCH gracza"<<identyfikator<<std::endl;
	}
	else
		this->porzednieRuchy.push_front(znaleziony);
	if(porzednieRuchy.size()==8)
		porzednieRuchy.pop_back();

	std::cout<<"Rozmiar TabliH"<<tablicaHistorii.size()<<std::endl;
	std::cout<<"Liczba odwiedzonych wierzcholkow ("<<identyfikator<<")"<<liczbaOdwiedzin<<std::endl;

	sredniaLiczbaOdwiedzin+=liczbaOdwiedzin;
	licznikUruchomien++;
	//std::cout<<identyfikator*rz.first<<std::endl;
	std::cout<<sredniaLiczbaOdwiedzin/licznikUruchomien<<std::endl;
	turaZakonczona=true;


}
void Komputer::WypelnijTabliceHistorii()
{
	//std::vector<std::pair<int, RUCH> > tempLista;
	for(int x1=0;x1<plansza->PobierzRozmiarBokuPlanszy();x1++)
		for(int y1=0;y1<plansza->PobierzRozmiarBokuPlanszy();y1++)
			for(int x2=0;x2<plansza->PobierzRozmiarBokuPlanszy();x2++)
				for(int y2=0;y2<plansza->PobierzRozmiarBokuPlanszy();y2++)
					tablicaHistorii.push_back(std::make_pair(0,RUCH(WSPOLRZEDNE_PLANSZY(x1,y1),WSPOLRZEDNE_PLANSZY(x2,y2))));	
	/*while(tempLista.size())
	{
		int los=rand()%tempLista.size();
		tablicaHistorii.push_back(tempLista[los]);
		tempLista.erase(tempLista.begin()+los);
	}*/
}

void Komputer::ZapiszTH()
{
	std::stringstream ss;
	ss<<"th"<<identyfikator<<plansza->PobierzRozmiarBokuPlanszy();
	std::ofstream th(ss.str().c_str());
	for(std::vector<std::pair<int, RUCH> >::iterator i=tablicaHistorii.begin();i!=tablicaHistorii.end();i++)
	{
		th<<i->first<<std::endl<<i->second.zrodlo<<std::endl<<i->second.cel<<std::endl;
	}
	th.close();	
}

void Komputer::LadujTH()
{
	std::stringstream ss;
	ss<<"th"<<identyfikator<<plansza->PobierzRozmiarBokuPlanszy();
	std::ifstream th(ss.str().c_str());
	if(!th.fail())
	{

	std::cout<<"Odczyt:" <<ss.str()<<std::endl;
	tablicaHistorii.clear();

	int val;
	WSPOLRZEDNE_PLANSZY a;
	WSPOLRZEDNE_PLANSZY b;
	int n=plansza->PobierzRozmiarBokuPlanszy();
	for(int i=0;i<n*n*n*n;i++)
	{
		th>>val;
		th>>a;
		th>>b;
		tablicaHistorii.push_back(std::make_pair(val,RUCH(a,b)));
	}

	th.close();	
	}
	

}

Komputer::Komputer(Plansza* plansza, TURA id,int glebokosc,int algorytm, bool_ ograniczCzas): 
Gracz(plansza,id), sredniaLiczbaOdwiedzin(0), licznikUruchomien(0),glebokoscMax(glebokosc), algorytm(algorytm), ograniczCzas(ograniczCzas)
	  {
	  int n=plansza->PobierzRozmiarBokuPlanszy();
	  tablicaHistorii.reserve( n*n*n*n);	/*WypelnijTabliceHistorii();*/ LadujTH();};